﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using NSubstitute;
using System.Web.Routing;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FreedBack.Tests
{
    public static class RouteTestingExtensions
    {
        private static HttpContextBase CreateHttpContext(string targetUrl = null, string httpMethod = "GET")
        {
            // create the mock request  
            var mockRequest = Substitute.For<HttpRequestBase>();
            mockRequest.AppRelativeCurrentExecutionFilePath.Returns(targetUrl);
            mockRequest.HttpMethod.Returns(httpMethod);

            // create the mock response 
            var mockResponse = Substitute.For<HttpResponseBase>();
            mockResponse.ApplyAppPathModifier(Arg.Any<string>()).Returns(ci => ci.Arg<string>());

            // create the mock context, using the request and response 
            var mockContext = Substitute.For<HttpContextBase>();
            mockContext.Request.Returns(mockRequest);
            mockContext.Response.Returns(mockResponse);

            // return the mocked context 
            return mockContext;
        }

        public static void AssertThatRoutesTo(this string url, string controller, string action, object routeProperties = null, string httpMethod = "GET")
        {
            //Arrange
            var routes = new RouteCollection();
            MvcApplication.RegisterRoutes(routes);

            //Act
            var result = routes.GetRouteData(CreateHttpContext(url, httpMethod));

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(TestIncomingRouteResult(result, controller, action, routeProperties));
        }

        public static void AssertThatRouteToFails(this string url, string httpMethod = "GET")
        {
            //Arrange
            var routes = new RouteCollection();
            MvcApplication.RegisterRoutes(routes);

            //Act
            var result = routes.GetRouteData((CreateHttpContext(url, httpMethod)));

            //Assert
            Assert.IsTrue(result == null || result.Route == null);
        }

        private static bool TestIncomingRouteResult(RouteData routeResult, string controller, string action, object propertySet = null)
        {
            Func<object, object, bool> isTheSame = (val1, val2) => { return StringComparer.InvariantCultureIgnoreCase.Compare(val1, val2) == 0; };

            var result = isTheSame(routeResult.Values["controller"], controller) && isTheSame(routeResult.Values["action"], action);

            if (propertySet != null)
            {
                var propInfo = propertySet.GetType().GetProperties();
                foreach (var currItem in propInfo)
                {
                    if (!routeResult.Values.ContainsKey(currItem.Name) && isTheSame(routeResult.Values[currItem.Name], currItem.GetValue(propertySet, null)))
                    {
                        result = false;
                        break;
                    }
                }
            }
            return result;
        }

        //static bool TestIncomingRouteResult(
        //    this RouteData routeResult,
        //    string controller,
        //    string action,
        //    object propertySet = null)
        //{
        //    Func<object, object, bool> valCompare = (v1, v2) => StringComparer.InvariantCultureIgnoreCase.Compare(v1, v2) == 0;

        //    bool result = valCompare(routeResult.Values["controller"], controller)
        //        && valCompare(routeResult.Values["action"], action);

        //    if (propertySet != null)
        //    {
        //        PropertyInfo[] propInfo = propertySet.GetType().GetProperties();
        //        foreach (PropertyInfo pi in propInfo)
        //        {
        //            if (!(routeResult.Values.ContainsKey(pi.Name)
        //                    && valCompare(routeResult.Values[pi.Name],
        //                        pi.GetValue(propertySet, null))))
        //            {

        //                result = false;
        //                break;
        //            }
        //        }
        //    }

        //    return result;
        //}

        //public static void RoutesTo(
        //    this string url,
        //    string controller,
        //    string action,
        //    object routeProperties = null,
        //    string httpMethod = "GET")
        //{
        //    // Arrange 
        //    var routes = new RouteCollection();
        //    MvcApplication.RegisterRoutes(routes);

        //    // Act - process the route 
        //    RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod));

        //    // Assert 
        //    Assert.IsNotNull(result);
        //    var message = string.Format(
        //        "URL '{0}' does not rosolve to '{1}Controller.{2}' (HttpMethod: '{3}')",
        //        url,
        //        controller,
        //        action,
        //        httpMethod);

        //    Assert.IsTrue(TestIncomingRouteResult(result, controller, action, routeProperties), message);
        //}

        //public static void RouteFails(this string url)
        //{
        //    // Arrange 
        //    var routes = new RouteCollection();
        //    MvcApplication.RegisterRoutes(routes);

        //    // Act - process the route 
        //    RouteData result = routes.GetRouteData(CreateHttpContext(url));

        //    // Assert 
        //    Assert.IsTrue(
        //        result == null || result.Route == null,
        //        string.Format("URL {0} routing should have failed; but did not", url));
        //}
    }
}
